""" SimVascular Python API

The sv package provides an application programming interface (API) for accessing core 
SimVascular functions. The API defines a number of Python modules and classes used to 
access, manipulate and create data for each of the path planning, segmentation, modeling, 
mesh generation and simulation steps in the SimVascular image-based modeling pipeline. 
Custom Python scripts can be written to augment the functionality provided by the 
SimVascular GUI and to automate modeling tasks for optimization, uncertainty quantification, 
and studies with large patient cohorts.

The sv package defines the following modules

  dmg - Access to SV Data Manager nodes 
  geometry - Functions operating on VTK PolyData objects 
  imaging - Classes and methods for imaging
  meshing - Classes and methods for meshing 
  modeling - Classes and methods for modeling 
  pathplanning - Classes and methods for path planning 
  segmentation - Classes and methods for segmentation 
  simulation - Classes and methods for simulation 
  vmtk - Interrface to several VMTK funcntions 

"""
# 
# Copyright (c) Stanford University, The Regents of the University of
#               California, and others.
#
# All Rights Reserved.
#
# See Copyright-SimVascular.txt for additional details.
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject
# to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#import os
#import math
import ctypes
import sys

# if script is launched embedded then
# argv can be unset which throws errors
# in some packages (e.g. tensorflow).
# Setting missing argv is a workaround.
import sys
if not hasattr(sys, 'argv'):
    sys.argv  = ['']

## Set the shared library extension.
#
if sys.platform == "darwin":
    ext = "dylib"
elif sys.platform.startswith("linux"):
    ext = "so"
elif sys.platform == "win32" or sys.platform =="cygwin":
    ext = "dll"
else:
    raise ValueError("System unrecognized")

def load_module(library_name, *args):
    ''' Load a module from a library and one or more module names.

    Args:
      *args: Module names.
    '''
    try:
        modules = []
        module_library = ctypes.PyDLL(library_name + "." + ext)
        for module_name in args:
            module = getattr(module_library, 'PyInit_' + module_name)
            module.restype = ctypes.py_object
            modules.append( module() )
        if len(modules) == 1:
            return modules[0]
        else:
            return tuple(modules)
    except:
        return None 

#----------------------------------------------------------
#                   Python 3.0
#----------------------------------------------------------

if sys.version_info >= (3,0):

    #------------
    # dmg module
    #------------
    try:
        dmg = load_module('liborg_sv_pythondatanodes', 'PyDmg')
    except:
        try:
            dmg = load_module('liborg_sv_pythondatanodes', 'PyDmg')
        except:
            print("Library '%s' not loaded." % 'liborg_sv_pythondatanodes') 

    project = load_module('liborg_sv_pythondatanodes', 'PyProject')

    #------------------------
    # Solid modeling modules 
    #------------------------
    modeling = load_module('lib_simvascular_python_api', 'PyModeling')
    #solid = load_module('lib_simvascular_solid', 'PySolid')
    #solid_polydata = load_module('lib_simvascular_polydata_solid', 'pySolidPolydata')

    from .loft_options import *

    # Don't expose the Model class; it is a base classe for the PolyData, OpenCascade 
    # and Parsolid classes.
    del modeling.Model

    # [TODO:DaveP] Do we need to expose 'parasolid_plugin', 'solid_occt' ?
    # 
    # Those classes are not accessed by name.
    #

    try:
        parasolid_plugin = load_module('lib_simvascular_parasolid_python_api', 'PyParasolidPlugin')
        #ParasolidPlugin = load_module('lib_simvascular_parasolid_solid', 'PyParasolidPlugin')
    except:
        pass

    try:
        solid_occt = load_module('lib_simvascular_opencascade_solid', 'pySolidOCCT')
    except:
        pass

    #-------------
    # Geom module
    #-------------
    try:
        geometry = load_module('lib_simvascular_python_api', 'PyGeometry')
    except:
        pass 

    #----------------
    # Imaging module
    #----------------
    imaging = load_module('lib_simvascular_python_api', 'PyImaging')

    #-----------------
    # Meshing modules 
    #-----------------
    meshing = load_module('lib_simvascular_python_api', 'PyMeshing')

    '''
    try:
        tetgen_meshing = load_module('lib_simvascular_tetgen_mesh', 'pyMeshTetgen')
    except:
        pass
    '''

    ## Try to load the MeshSim mesh generator plugin.
    #
    try:
        meshsim_plugin = load_module('lib_simvascular_meshsim_python_api', 'PyMeshSimPlugin')

        # Define the MeshSimOptions class used to store MeshSim options.
        from .meshsim_options import *
        meshing.MeshSimOptions = MeshSimOptions
        del MeshSimOptions

    except:
        pass

    # Define the MeshSimOptions class used to store MeshSim options.
    #
    # [TODO:DaveP] This is just for testing on Mac.
    from .meshsim_options import *
    meshing.MeshSimOptions = MeshSimOptions
    del MeshSimOptions

    try:
        mesh_utils = load_module('lib_simvascular_python_api', 'PyMeshUtils')
    except:
        pass

    # Adaptive meshing modules. 
    '''
    mesh_adapt = load_module('lib_simvascular_adaptor', 'pyMeshAdapt')
    try:
        tetgen_adapt = load_module('lib_simvascular_tetgen_adaptor', 'pyTetGenAdapt')
    except:
        pass 
    try:
        MeshSimAdapt = load_module('lib_simvascular_meshsim_adaptor', 'pyMeshSimAdapt')
    except Exception as e:
        pass 
    '''

    #---------------------
    # pathplanning module
    #---------------------
    pathplanning = load_module('lib_simvascular_python_api', 'PyPathplanning')

    #----------------------
    # Segmentation modules
    #----------------------
    # Load several modules at a time.
    #
    seg_lib = 'lib_simvascular_segmentation'
    python_api_lib = 'lib_simvascular_python_api'
    segmentation = load_module(python_api_lib, 'PySegmentation') 
    #contour,circle_contour,levelset_contour = load_module(seg_lib, 'PyContour', 'pyCircleContour', 'pylevelSetContour') 
    #polygon_contour,spline_polygon_contour = load_module(seg_lib, 'pyPolygonContour', 'pySplinePolygonContour') 
    #threshold_contour = load_module(seg_lib, 'pyThresholdContour') 

    #-------------------
    # Simulation module
    #-------------------
    simulation = load_module(python_api_lib, 'PySimulation')
    from .simulation_parameters import *
    simulation.FluidParameters = FluidParameters
    del FluidParameters
    simulation.ROMParameters = ROMParameters
    del ROMParameters
    del simulation_parameters

    #-------------------
    # VMTK utils module
    #-------------------
    try:
        vmtk = load_module(python_api_lib, 'PyVmtk')
    except:
        print("VMTK is not available!")

    #------------
    # ITK module
    #------------
    # [TODO:DaveP] don't expose this for now. 
    """
    try:
        itk_levelset = load_module('lib_simvascular_itk_lset', 'pyItkls') 
    except:
        pass 
    """

    #----------------
    # Imaging module
    #----------------
    image = load_module('lib_simvascular_image', 'pyImage')

    #-------------------
    # Math utils module
    #-------------------
    # [DaveP] Don't expose this module, not really of much use?
    # math = load_module(python_api_lib, 'PyMath') 

    #-------------------
    # Repository module
    #-------------------
    #repository = load_module('lib_simvascular_repository', 'pyRepository')

    ## Remove the varialbles used here so they don't show up in the doc. 
    del ext 
    del load_module 


#----------------------------------------------------------
#                   Python 2.0
#----------------------------------------------------------

else:
    myDll=ctypes.PyDLL('lib_simvascular_solid.' + ext)
    myDll.initpySolid()
    import pySolid as Solid
    myDll=ctypes.PyDLL('lib_simvascular_polydata_solid.' + ext)
    myDll.initpySolidPolydata()
    import pySolidPolydata as SolidPolyData
    try:
        myDll=ctypes.PyDLL('lib_simvascular_parasolid_solid.' + ext)
        myDll.initpySolidParasolid()
        import pySolidParasolid as SolidParaSolid
    except:
        pass
    myDll=ctypes.PyDLL('lib_simvascular_repository.' + ext)
    myDll.initpyRepository()
    import pyRepository as Repository

    ## Mesh modules.
    myDll = ctypes.PyDLL('lib_simvascular_mesh.' + ext)
    myDll.initpyMeshObject()
    #import pyMeshObject as MeshObject

    try:
        myDll=ctypes.PyDLL('lib_simvascular_tetgen_mesh.' + ext)
        myDll.initpyMeshTetgen()
        import pyMeshTetgen as MeshTetGen
    except:
        print("TetGen is not available!")
    try:
        myDll=ctypes.PyDLL('lib_simvascular_meshsim_mesh.' + ext)
        myDll.initpyMeshSim()
        import pyMeshSim as MeshSim
    except Exception as e:
        pass
    myDLL=ctypes.PyDLL('lib_simvascular_utils.' + ext)
    myDLL.initpyMath()
    import pyMath as Math

    ## Segmentation modules
    #
    myDll=ctypes.PyDLL('lib_simvascular_segmentation.' + ext)
    myDll.initpyContour()
    myDll.initpyThresholdContour()
    myDll.initpylevelSetContour()
    myDll.initpyPolygonContour()
    myDll.initpyCircleContour()
    myDll.initpySplinePolygonContour()
    #import pyContour as Contour
    import pyThresholdContour as ThresholdContour
    import pylevelSetContour as LevelSetContour
    import pyCircleContour as CircleContour
    import pyPolygonContour as PolygonContour
    import pySplinePolygonContour as SplinePolygonContour
    try:
        myDll=ctypes.PyDLL('lib_simvascular_vmtk_utils.' + ext)
        myDll.initpyVMTKUtils()
        import pyVMTKUtils as VMTKUtils
    except:
        print("VMTK is not available!")
    try:
        myDll=ctypes.PyDLL('lib_simvascular_opencascade_solid.' + ext)
        myDll.initpySolidOCCT()
        import pySolidOCCT as SolidOCCT
    except:
        print("OpenCASCADE is not available!")

    try:
        myDll=ctypes.PyDLL('lib_simvascular_mmg_mesh.' + ext)
        myDll.initpyMeshUtil()
        #import pyMeshUtil as MeshUtil
    except:
        print("MMG is not available!")

    try:
        myDll=ctypes.PyDLL('lib_simvascular_itk_lset.' + ext)
        myDll.initpyItkls()
        import pyItkls as Itkls
    except:
        print("ITK is not available!")
    myDll=ctypes.PyDLL('lib_simvascular_adaptor.' + ext)
    myDll.initpyMeshAdapt()
    import pyMeshAdapt as MeshAdapt
    try:
        myDll=ctypes.PyDLL('lib_simvascular_tetgen_adaptor.' + ext)
        myDll.initpyTetGenAdapt()
        import pyTetGenAdapt as TetGenAdapt
    except:
        print("TetGen adaptor is not available!")
    try:
        myDll=ctypes.PyDLL('lib_simvascular_meshsim_adaptor.' + ext)
        myDll.initpyMeshSimAdapt()
        import pyMeshSimAdapt as MeshSimAdapt
    except Exception as e:
        pass
    try:
        myDll=ctypes.PyDLL('lib_simvascular_geom.' + ext)
        myDll.initpyGeom()
        import pyGeom as Geom
    except:
        print("Geometry functions are not available!")
    myDll=ctypes.PyDLL('lib_simvascular_image.' + ext)
    myDll.initpyImage()
    import pyImage as Image
    myDll=ctypes.PyDLL('lib_simvascular_path.' + ext)
    myDll.initpyPath()
    #import pyPath as Path
    try:
      myDll=ctypes.PyDLL('liborg_sv_pythondatanodes.' + ext)
      myDll.initpyGUI()
      import pyGUI as GUI
    except:
      try:
        myDll=ctypes.PyDLL('org_sv_pythondatanodes.' + ext)
        myDll.initpyGUI()
        import pyGUI as GUI
      except:
        print("org_sv_pythondatanodes was not found!")
